--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 5014446b41ad69f1b63c592d286e576d7109c9b2
Parents : 6008b22
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-07-08T09:26:40-05:00
feat(messages): update MessageReactionsOverlay to support reaction chips and limit visible reactions
Changes
1 files changed, 31 insertions(+), 9 deletions(-)
Diff
diff --git a/meshchatx/src/frontend/components/messages/MessageReactionsOverlay.vue b/meshchatx/src/frontend/components/messages/MessageReactionsOverlay.vue
index 82069472..63fddc4c 100644
--- a/meshchatx/src/frontend/components/messages/MessageReactionsOverlay.vue
+++ b/meshchatx/src/frontend/components/messages/MessageReactionsOverlay.vue
@@ -3,29 +3,30 @@
<template>
<div
v-if="(reactions?.length ?? 0) > 0 || showReactButton"
- class="pointer-events-auto absolute z-20 flex w-fit max-w-[calc(100%-0.75rem)] flex-wrap items-center gap-0.5"
+ class="pointer-events-auto absolute z-20 flex w-fit max-w-[calc(100%-0.75rem)] flex-nowrap items-center gap-0.5"
:class="[
isOutbound ? 'right-2 justify-end' : 'left-2 justify-start',
elevated ? 'bottom-9 translate-y-0' : 'bottom-0 translate-y-1/2',
]"
>
<span
- v-for="(r, ridx) in reactions"
- :key="r.reactionHash || ridx"
- class="inline-flex min-h-4.5 min-w-4.5 cursor-default select-none items-center justify-center rounded-full border border-gray-200/90 bg-white px-1 py-0 text-sm leading-none shadow-sm ring-1 ring-white/90 dark:border-zinc-600/90 dark:bg-zinc-900 dark:ring-zinc-800/90"
+ v-for="(chip, chipIdx) in reactionChips"
+ :key="chip.key"
+ class="inline-flex min-h-4.5 min-w-4.5 shrink-0 cursor-default select-none items-center justify-center rounded-full border border-gray-200/90 bg-white px-1 py-0 text-sm leading-none shadow-sm ring-1 ring-white/90 dark:border-zinc-600/90 dark:bg-zinc-900 dark:ring-zinc-800/90"
+ :class="chip.kind === 'more' ? 'text-[10px] font-semibold text-gray-500 dark:text-zinc-400' : ''"
:style="{
- order: isOutbound ? ridx + 2 : ridx + 1,
+ order: isOutbound ? chipIdx + 2 : chipIdx + 1,
}"
- :title="cv.reactionReactorLabel(r.sender)"
- >{{ r.emoji }}</span
+ :title="chip.kind === 'reaction' ? cv.reactionReactorLabel(chip.reaction.sender) : ''"
+ >{{ chip.kind === "more" ? `+${hiddenReactionCount}` : chip.reaction.emoji }}</span
>
<button
v-if="showReactButton"
type="button"
- class="inline-flex items-center justify-center rounded-full border border-dashed border-gray-300 bg-white/95 text-xs leading-none text-gray-400 opacity-0 shadow-sm ring-1 ring-white/90 hover:border-gray-400 hover:text-gray-600 hover:bg-gray-50 group-hover:opacity-100 dark:border-zinc-600 dark:bg-zinc-900/95 dark:text-zinc-500 dark:ring-zinc-800/90 dark:hover:border-zinc-500 dark:hover:text-zinc-300 dark:hover:bg-zinc-800 transition-colors"
+ class="inline-flex shrink-0 items-center justify-center rounded-full border border-dashed border-gray-300 bg-white/95 text-xs leading-none text-gray-400 opacity-0 shadow-sm ring-1 ring-white/90 hover:border-gray-400 hover:text-gray-600 hover:bg-gray-50 group-hover:opacity-100 dark:border-zinc-600 dark:bg-zinc-900/95 dark:text-zinc-500 dark:ring-zinc-800/90 dark:hover:border-zinc-500 dark:hover:text-zinc-300 dark:hover:bg-zinc-800 transition-colors"
:class="(reactions?.length ?? 0) > 0 ? 'min-h-4.5 min-w-4.5 px-1 py-0' : 'h-4 w-4 min-h-0 p-0'"
:style="{
- order: isOutbound ? 1 : (reactions?.length ?? 0) + 1,
+ order: isOutbound ? 1 : reactionChips.length + 1,
}"
:title="$t('messages.react')"
@click.stop="cv.openReactionPicker(chatItem)"
@@ -38,6 +39,8 @@
<script>
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
+const MAX_VISIBLE_REACTIONS = 4;
+
export default {
name: "MessageReactionsOverlay",
components: {
@@ -69,5 +72,24 @@ export default {
default: false,
},
},
+ computed: {
+ visibleReactions() {
+ return (this.reactions || []).slice(0, MAX_VISIBLE_REACTIONS);
+ },
+ hiddenReactionCount() {
+ return Math.max(0, (this.reactions?.length ?? 0) - MAX_VISIBLE_REACTIONS);
+ },
+ reactionChips() {
+ const chips = this.visibleReactions.map((r, idx) => ({
+ kind: "reaction",
+ key: r.reactionHash || `reaction-${idx}`,
+ reaction: r,
+ }));
+ if (this.hiddenReactionCount > 0) {
+ chips.push({ kind: "more", key: "reaction-more" });
+ }
+ return chips;
+ },
+ },
};
</script>
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────